package kr.hyosang;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

public class HttpUtil
{
    private final String DEFAULT_ENCODING = "US-ASCII";
    private URL connUrl = null;
    private URLConnection conn = null;
    private String strErrorMsg = null;
    private boolean bError = false;
    private byte [] responseData;
    private OnProgressListener listener = null;
    private HashMap<String, String> mPostVal;
    private HashMap<String, String> mReqHeader;
    
    public interface OnProgressListener {
        public boolean onProgress(HttpUtil httpUtil, int nReadBytes, int nTotalBytes);
    }
    
    public HttpUtil() {
        mPostVal = new HashMap<String, String>();
        mReqHeader = new HashMap<String, String>();
    }
    
    public byte[] getResponseData() {
        return responseData;
    }
    
    public URLConnection getConnection() {
        return conn;
    }
    
    public void setRequestProperty(String name, String value) {
        mReqHeader.put(name, value);
    }
    
    public String getHeaderField(String name) {
        return conn.getHeaderField(name);
    }
    
    public List<String> getHeaderFields(String name) {
        return conn.getHeaderFields().get(name);
    }
    
    
    public void addPostValue(String name, String value) {
        mPostVal.put(name, value);
    }
    
    public void setURL(String url) throws MalformedURLException {
        this.connUrl = new URL(url);
    }
    
    public String getHttpErrorMessage() {
        if(conn == null) return "No Connection Established";
        
        return strErrorMsg;
    }
    
    public void setListener(OnProgressListener l) {
        this.listener = l;
    }
    
    public boolean isError() {
        return bError;
    }
    
    public void process() {
        if(connUrl == null) return;
        this.getBytes();
    }
    
    public String getHtml() {
        String contentEncoding = getEncodingFromContentType(conn.getHeaderField("Content-Type"));
        
        try
        {
            return new String(responseData, contentEncoding);
        } catch (UnsupportedEncodingException e)
        {
            try
            {
                return new String(responseData, DEFAULT_ENCODING);
            } catch (UnsupportedEncodingException e1)
            {
                e1.printStackTrace();
                return "";
            }
        }
    }
    
    private String getPostString() {
        if(mPostVal.isEmpty()) {
            return "";
        }
        
        StringBuffer sb = new StringBuffer();
        Set<String> key = mPostVal.keySet();
        for(String k : key) {
            try
            {
                sb.append(k);
                sb.append("=");
                sb.append(URLEncoder.encode(mPostVal.get(k), "EUC-KR"));
                sb.append("&");
            } catch (UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }
        }
        
        return sb.toString();
    }
    
    private byte[] getBytes() {
        try {
            InputStream is = getHttpStream();
            
            byte [] buf = new byte[512];
            byte [] totalBuf = null;
            int nRead = 0;
            int nDestPos;
            
            while(true) {
                nRead = is.read(buf);
                
                if(nRead > 0) {
                    if(totalBuf == null) {
                        totalBuf = new byte[nRead];
                        nDestPos = 0;
                    }else {
                        nDestPos = totalBuf.length;
                        byte [] tmpBuf = new byte[totalBuf.length + nRead];
                        System.arraycopy(totalBuf, 0, tmpBuf, 0, totalBuf.length);
                        totalBuf = tmpBuf;
                    }
                    
                    System.arraycopy(buf, 0, totalBuf, nDestPos, nRead);
                    
                    if(listener != null) {
                        if(!listener.onProgress(this, totalBuf.length, conn.getHeaderFieldInt("Length", -1))) {
                            break;
                        }
                    }
                }else {
                    break;
                }
            }
            this.responseData = totalBuf;
            
            return totalBuf;
        }catch(IOException e) {
            strErrorMsg = e.getMessage();
            bError = true;
        }catch(Exception e) {
            e.printStackTrace();
        }
        
        return null;
        
    }
    
    private InputStream getHttpStream() {
        try {
            if(connUrl == null) return null;
            
            conn = connUrl.openConnection();
            
            conn.setDoInput(true);
            conn.setDoOutput(true);
            
            Set<String> key = mReqHeader.keySet();
            for(String k : key) {
                conn.setRequestProperty(k, mReqHeader.get(k));
            }
            
            String postStr = getPostString();
            
            OutputStream os = conn.getOutputStream();
            if(!"".equals(postStr)) {
                os.write(postStr.getBytes());
            }
            os.flush();
            
            
            InputStream is = conn.getInputStream();

            bError = false;
            return is;
        }catch(FileNotFoundException e) {
            //Error 404
            bError = true;
        }catch(IOException e) {
            strErrorMsg = e.toString();
            bError = true;
        }
        
        return null;
        
    }
    
    
    private String getEncodingFromContentType(String contentType) {
        if(contentType == null || contentType.length() == 0) {
            return DEFAULT_ENCODING;
        }
        
        if(contentType.startsWith("text/")) {
            StringTokenizer tok = new StringTokenizer(contentType, ";");
            if(tok.countTokens() == 2) {
                tok.nextToken();    //pass first
                StringTokenizer t = new StringTokenizer(tok.nextToken(), "=");
                
                if(t.countTokens() == 2) {
                    t.nextToken();
                    return t.nextToken();
                }
            }
        }
        
        return DEFAULT_ENCODING;
    }
}
